using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Collections;
using Microsoft.Xna.Framework.Input;

namespace Gremlin.Components
{
    public delegate void ConsoleCommand(string command);

    /// <summary>
    /// Console for displaying text, renders to offscreen surface
    /// renders offscreen surface with alpha blending to screen
    /// </summary>
    public class Console : Microsoft.Xna.Framework.DrawableGameComponent
    {
        public event ConsoleCommand commandEvent;

        private RenderTarget2D surface;
        private Vector2 size;
        private GraphicsDevice graphicsDevice;
        private ContentManager content;
        private BasicEffect effect;
        private SpriteBatch sBatch;
        private SpriteFont font;
        private ArrayList lines;
        private string cLine;
        private Microsoft.Xna.Framework.Game game;

        private bool visible;
        private Vector2 bottomLeft, topRight;

        private bool toggleKeyDown;
        private Keys toggleKey;

        private Keys[] previouslyPressed;

        public Console(Microsoft.Xna.Framework.Game game) : base(game)
        {
            this.game = game;
            this.size = new Vector2(512, 512);
            lines = new ArrayList();
            toggleKey = Keys.F1;
            cLine = "";
        }

        public override void Initialize()
        {
            graphicsDevice = game.GraphicsDevice;
            QuadRenderer.Initialize(game);

            surface = new RenderTarget2D(graphicsDevice, (int)size.X, (int)size.Y, 1, SurfaceFormat.Color);
            content = new ContentManager(game.Services);

            effect = new BasicEffect(graphicsDevice, null);
            effect.Alpha = 0.5f;
            effect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
            effect.SpecularColor = new Vector3(1.0f, 1.0f, 1.0f);
            effect.SpecularPower = 5.0f;
            effect.AmbientLightColor = new Vector3(0.0f, 0.0f, 0.0f);
            effect.LightingEnabled = false;
            effect.TextureEnabled = true;

            font = content.Load<SpriteFont>("Content\\Fonts\\console");
            sBatch = new SpriteBatch(graphicsDevice);

            visible = false;

            bottomLeft = new Vector2(-1, 0);
            topRight = new Vector2(0, 1);
        }

        private void UpdateSurface()
        {
            RenderTarget old = graphicsDevice.GetRenderTarget(0);

            graphicsDevice.SetRenderTarget(0, surface);

            graphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0);

            sBatch.Begin();
            RenderText();
            sBatch.End();

            //Render states needed because DrawString screws with something
            graphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            graphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            graphicsDevice.RenderState.DepthBufferEnable = true;

            graphicsDevice.SetRenderTarget(0, old as RenderTarget2D);
        }

        private void RenderText()
        {
            int height = (int)size.Y-50;
            int delCount = 0;
            sBatch.DrawString(font, cLine, new Vector2(10, height + 22), Color.WhiteSmoke);
            sBatch.DrawString(font, "______________________________________________________________________________", new Vector2(0, height), Color.WhiteSmoke);
            for (int i = lines.Count - 1; i >= 0; i--)
            {
                string line = lines[i] as string;
                if (height < 0)
                {
                    ++delCount;
                    continue;
                }
                sBatch.DrawString(font, line, new Vector2(10, height), Color.WhiteSmoke);
                height -= 20;
            }
            if (delCount > 0)
            {
                lines.RemoveRange(0, delCount);
            }

        }

        private void RenderSurface()
        {
            effect.Texture = surface.GetTexture();

            graphicsDevice.RenderState.AlphaBlendEnable = true;
            graphicsDevice.RenderState.DepthBufferEnable = false;
            effect.Begin();
            effect.Techniques[0].Passes[0].Begin();

            QuadRenderer.Render(bottomLeft, topRight);
            
            effect.Techniques[0].Passes[0].End();
            effect.End();
            graphicsDevice.RenderState.DepthBufferEnable = true;
        }


        public override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(toggleKey) && !toggleKeyDown)
            {
                IsVisible = !IsVisible;
            }
            toggleKeyDown = Keyboard.GetState().IsKeyDown(toggleKey);

            if (visible)
                StreamingTyping();
            else
                previouslyPressed = new Keys[0];

            base.Update(gameTime);
        }

        public void StreamingTyping()
        {
            Keys[] cPressed = Keyboard.GetState().GetPressedKeys();

            foreach (Keys key in cPressed)
            {
                bool found = false;
                if (key == toggleKey)
                    continue;

                foreach (Keys compare in previouslyPressed)
                    if (compare == key)
                    {
                        found = true;
                        break;
                    }
                if (!found) // newly pressed
                {
                    if (key == Keys.Enter)
                    {
                        if (cLine != "")
                        {
                            commandEvent(cLine);
                            AddLine(cLine);
                            cLine = "";
                            UpdateSurface();
                        }
                    }else
                        if (key == Keys.Back)
                        {
                            if (cLine.Length > 0)
                                cLine = cLine.Substring(0, cLine.Length - 1);
                            UpdateSurface();
                        }
                        else
                        {
                            if (key == Keys.Space)
                                cLine += " ";
                            else
                                cLine += key.ToString();
                            UpdateSurface();
                        }
                }
            }
            previouslyPressed = cPressed;
        }

        public override void Draw(GameTime gameTime)
        {
            if (visible)
                RenderSurface();

            base.Draw(gameTime);
        }

        public void SetRenderDimensions(Vector2 topLeftFraction, Vector2 bottomRightFraction)
        {
            float left = (topLeftFraction.X * 2) - 1;
            float right = (bottomRightFraction.X * 2) - 1;
            float top = (1 - topLeftFraction.Y) * 2 - 1;
            float bottom = (1 - bottomRightFraction.Y) * 2 - 1;

            bottomLeft = new Vector2(left, bottom);
            topRight = new Vector2(right, top);
        }

        public void AddLine(string line)
        {
            lines.Add(line);
            UpdateSurface();
        }

        public bool IsVisible
        {
            get { return visible; }
            set { visible = value; }
        }

        public Keys ToggleKey
        {
            get { return toggleKey; }
            set { toggleKey = value; }
        }
    }

    public class QuadRenderer
    {
        static VertexDeclaration vertexDecl = null;
        static VertexPositionTexture[] verts = null;
        static short[] ib = null;
        static Microsoft.Xna.Framework.Game game;

        public static void Initialize(Microsoft.Xna.Framework.Game g)
        {
            game = g;

            IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)
                game.Services.GetService(typeof(IGraphicsDeviceService));

            vertexDecl = new VertexDeclaration(graphicsService.GraphicsDevice,
                VertexPositionTexture.VertexElements);

            verts = new VertexPositionTexture[]
            {
                new VertexPositionTexture(
                    new Vector3(0,0,0),
                    new Vector2(1,1)),
                new VertexPositionTexture(
                    new Vector3(0,0,0),
                    new Vector2(0,1)),
                new VertexPositionTexture(
                    new Vector3(0,0,0),
                    new Vector2(0,0)),
                new VertexPositionTexture(
                    new Vector3(0,0,0),
                    new Vector2(1,0))
            };

            ib = new short[] { 0, 1, 2, 2, 3, 0 };
        }

        public static void Render(Vector2 v1, Vector2 v2)
        {
            IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)
                game.Services.GetService(typeof(IGraphicsDeviceService));

            GraphicsDevice device = graphicsService.GraphicsDevice;
            device.VertexDeclaration = vertexDecl;

            verts[0].Position.X = v2.X;
            verts[0].Position.Y = v1.Y;

            verts[1].Position.X = v1.X;
            verts[1].Position.Y = v1.Y;

            verts[2].Position.X = v1.X;
            verts[2].Position.Y = v2.Y;

            verts[3].Position.X = v2.X;
            verts[3].Position.Y = v2.Y;

            device.DrawUserIndexedPrimitives<VertexPositionTexture>
                (PrimitiveType.TriangleList, verts, 0, 4, ib, 0, 2);
        }


        public static void Dispose()
        {
            if (vertexDecl != null)
                vertexDecl.Dispose();

            vertexDecl = null;
        }
    }
}
